<template>
  <div class="canvas-test-event">
    <h1>{{ message }}</h1>
    <h2>一、与鼠标的交互大致为三类：</h2>
    <ol>
        <li>包围盒法（即简单处理，包围图形的最小规则范围，适合简单图形和fill填充的图形，不太适合路径图形）</li>
        <li>利用canvas提供的API：isPointInStroke、isPointInPath</li>
        <li>通过getImageData，变相获取位置，比较适合单一不重复颜色</li>
    </ol>
    <section class="contain-canvas">
        <h3>1、包围盒法</h3>
        <p>大概就是圆形和矩形两类</p>
        <img src="../../images/canvas/shape.png" />
        <p>以矩形为例，点击小矩形使其缩小再放大</p>
        <canvas id="contain" width="300px" height="240px" @click="testContain"></canvas>
        <div class="info">
            你点击的位置是(相对于canvas左上角):
            <p v-html="contianPosition"></p>
        </div>
        <div>
            <p>部分代码</p>
            <code>
                <pre>
                    testContain (e: MouseEvent): void {
                        let str: string  = '不在小矩形上';

                        if (e.offsetX &gt; 50 && e.offsetX &lt; 150 && e.offsetY &gt; 50 && e.offsetY &lt; 150) {
                            str = '在小矩形上';
                            this.animationId = window.requestAnimationFrame(this.expandRect);
                        } else {
                            this.animationId = window.requestAnimationFrame(this.shrinkRect);
                        }

                        this.contianPosition = `x:${e.offsetX}, y:${e.offsetY} &lt;br&gt; ${str}`;
                    }
                </pre>
            </code>
        </div>
    </section>
    <section class="contain-canvas">
        <h3>2、isPointInStroke、isPointInPath 判断当前鼠标位置是否在形状内或在路径上</h3>
        <p>以sPointInStroke为例，判断鼠标是否在圆环上</p>
        <canvas id="circleContain" width="300px" height="300px" @click="testCircle"></canvas>
        <div class="info">
            你点击的位置是:
            <p v-html="circlePosition"></p>
        </div>
        <div>
            <p>部分代码：（isPointInPath 同理）</p>
            <code>
                <pre>
                testCircle (e: MouseEvent): void {
                    // 重新画一遍 不闭合，不画到画布上
                    const canvas = document.getElementById('circleContain') as HTMLCanvasElement;　
                    let context = canvas.getContext('2d') as CanvasRenderingContext2D;
                    context.lineWidth = 20;
                    context.arc(150, 150, 140, 0, 2*Math.PI, false);

                    let str: string  = '不在圆上';
                    if (context.isPointInStroke(e.offsetX, e.offsetY)) {
                        str = '在圆上';
                    }

                    this.circlePosition = `x:${e.offsetX}, y:${e.offsetY} &lt;br&gt; ${str}`;
                }
                </pre>
            </code>
        </div>
    </section>
    <section class="contain-canvas-image">
        <h3>3、通过getImageData，变相获取位置，比较适合单一不重复颜色</h3>
        <canvas id="imageContain" width="432px" height="324px"></canvas>
        <div class="info">
            你鼠标的位置是:
            <p class="image-color">
                <span :style="{background: imgStyleColor}"></span>
                {{imgStyleColor}}
            </p>
        </div>
        <div>
            <p>部分代码：</p>
            <code>
                <pre>
                    canvas.addEventListener('mousemove', (e: MouseEvent) => {
                        let params: Array&lt;number&gt; = [];
                        // 必须push 不能直接赋值
                        (context.getImageData(e.offsetX, e.offsetY, 1, 1).data || []).forEach(item => {
                            params.push(item);
                        });

                        this.imgStyleColor = `rgb(${params[0]}, ${params[1]}, ${params[2]})`;
                    });
                </pre>
            </code>
        </div>
    </section>
    <h2>二、与键盘的交互</h2>
    <p>canvas 监听键盘输入事件，直接监听不被触发,因为不是输入类型的标签</p>
    <div>
        <p>以下代码不会触发keup事件：</p>
        <code>
            <pre>
                &lt;canvas style="height:300px;width:300px;background:red;" @keyup="testKeyup" &gt;&lt;/canvas&gt;
                testKeyup (): void {
                    console.error(123)
                }

                // 同时直接绑定也是不起作用的
                (document.getElementById('canvass') as HTMLCanvasElement).addEventListener('keyup', this.doKeyDown);
            </pre>
        </code>
    </div>
    <p>解决方案大概有两种</p>
    <ol>
        <li>绑定在window或者document上，缺点就是不只给canvas 绑了事件</li>
        <li>可以通过给canvas设置 tabindex 属性的方式获得焦点</li>
    </ol>
    <section>
        <h3>1、绑定在window或者document上，缺点就是不只给canvas 绑了事件</h3>
        <canvas id="documentOrWindow" class="documentOrWindow" height="300px" width="900px"></canvas>
        <div id="test">
            <p>部分代码：</p>
            <code>
                <pre>
                    // val: Object  { console.error((val as any).key) } 或者 val: { [key: string] : '' }
                    doKeyDown (val: KeyboardEvent): void {
                        const canvas = document.getElementById('documentOrWindow') as HTMLCanvasElement;
                        const context = canvas.getContext('2d') as CanvasRenderingContext2D;
                        context.save();
                        context.beginPath();
                        
                        let color = this.rgb() as string;
                        context.fillStyle = color;
                        context.textAlign = 'center';
                        context.font = `${(Math.random() * 30) + 20}px sans-serif`;

                        // 这里 any 可能不太好
                        let positon = this.radomPosition() as any;
                        context.fillText(val.key, positon.x, positon.y);
                        context.restore();
                    }
                </pre>
            </code>
        </div>
    </section>
    <section>
        <h3>2、通过给canvas设置 tabindex 属性的方式获得焦点</h3>
        <canvas id="tabindex" class="documentOrWindow" height="300px" width="900px" @keyup="tabIndexTest"></canvas>
        <div id="test">
            <p>部分代码：</p>
            <code>
                <pre>
        &lt;canvas id="tabindex" class="documentOrWindow" height="300px" width="900px" @keyup="tabIndexTest"&gt;&lt;/canvas&gt;
        drawTabCanvas (): void {
            const canvas = document.getElementById('tabindex') as HTMLCanvasElement;
            canvas.setAttribute('tabindex', '0');
            canvas.addEventListener('click', () => {
                canvas.focus();
            });
            canvas.focus();
        }
        tabIndexTest (e: KeyboardEvent): void {
            const canvas = document.getElementById('tabindex') as HTMLCanvasElement;
            const context = canvas.getContext('2d') as CanvasRenderingContext2D;
            context.save();
            context.beginPath();
            
            let color = this.rgb() as string;
            context.fillStyle = color;
            context.textAlign = 'center';
            context.font = `${(Math.random() * 30) + 20}px sans-serif`;

            // 这里 any 可能不太好
            let positon = this.radomPosition() as any;
            context.fillText(e.key, positon.x, positon.y);
            context.restore();
        }
                </pre>
            </code>
        </div>
    </section>
    <p class="page-end">The End</p>
  </div>
</template>

<script lang="ts">
    import Vue from 'vue';
    import Component from 'vue-class-component';
    import imageFile from '../../images/canvas/getImageData.jpeg';

    // 定义接口 之后把返回值有x，y 的变量 赋予该类型不会报错
    interface Position {
        x: number;
        y: number;
    }

    @Component({
        // 所有的组件选项都可以放在这里
    })
    export default class Canvas extends Vue {
        // data 在这
        message: string = 'canvas 鼠标以及键盘交互方案'

        // 在矩形内大概范围判断
        contianPosition: string = ''
        width: number = 0
        animationId: number = 0
        drawContain (): void {
            this.drawRect(50,50,100,100);
        }
        drawRect (x: number, y: number, width: number, height: number): void {
            const canvas = document.getElementById('contain') as HTMLCanvasElement;
            let context = canvas.getContext('2d') as CanvasRenderingContext2D;
            context.clearRect(0, 0, 300, 300)
            context.save();
            context.beginPath();
            context.fillStyle = '#54d8c9';
            context.fillRect(x, y, width, height);
            context.strokeStyle = '#008af5';
            context.lineWidth = 3;
            context.strokeRect(3, 3, 294, 234);
            context.closePath();
            context.restore();
        }
        expandRect (): void {
            if (this.width > 50) {
                window.cancelAnimationFrame(this.animationId);

                return;
            }
            this.drawRect(50, 50, 100 + this.width, 100 + this.width);

            this.animationId = window.requestAnimationFrame(this.expandRect);
            this.width += 1;
        }
        shrinkRect (): void {
            if (this.width < 1) {
                window.cancelAnimationFrame(this.animationId);

                return;
            }
            this.drawRect(50, 50, 100 + this.width, 100 + this.width);

            this.animationId = window.requestAnimationFrame(this.shrinkRect);
            this.width -= 1;
        }
        testContain (e: MouseEvent): void {
            let str: string = '不在小矩形上';

            if (e.offsetX > 50 && e.offsetX < 150 && e.offsetY > 50 && e.offsetY < 150) {
                str = '在小矩形上';
                this.animationId = window.requestAnimationFrame(this.expandRect);
            } else {
                this.animationId = window.requestAnimationFrame(this.shrinkRect);
            }

            this.contianPosition = `x:${e.offsetX}, y:${e.offsetY} <br> ${str}`;
        }

        // 圆形路径
        circlePosition: string = ''
        drawCircle (): void {
            const canvas = document.getElementById('circleContain') as HTMLCanvasElement;
            let context = canvas.getContext('2d') as CanvasRenderingContext2D;

            context.beginPath();
            context.lineWidth = 20; // 设置线宽
            context.strokeStyle = '#F5D358';
            context.arc(150, 150, 140, 0, 2*Math.PI, false);
            context.stroke();
            context.closePath();
            context.restore();
        }
        testCircle (e: MouseEvent): void {
            // 重新画一遍 不闭合，不画到画布上
            const canvas = document.getElementById('circleContain') as HTMLCanvasElement;
            let context = canvas.getContext('2d') as CanvasRenderingContext2D;
            context.lineWidth = 20;
            context.arc(150, 150, 140, 0, 2*Math.PI, false);

            let str: string = '不在圆上';
            if (context.isPointInStroke(e.offsetX, e.offsetY)) {
                str = '在圆上';
            }

            this.circlePosition = `x:${e.offsetX}, y:${e.offsetY} <br> ${str}`;
        }

        // 图片获取颜色
        imgStyleColor: string = 'rgb(1,1,1)'
        drawImage (): void {
            const canvas = document.getElementById('imageContain') as HTMLCanvasElement;
            let context = canvas.getContext('2d') as CanvasRenderingContext2D;

            let img = document.createElement('img') as HTMLImageElement;
            img.src = imageFile;

            img.onload = function () {
                context.beginPath();
                context.drawImage(img, 0, 0, 1440, 1080, 0, 0, 432, 324);
                context.restore();
            }

            canvas.addEventListener('mousemove', (e: MouseEvent) => {
                let params: Array<number> = [];
                // 必须push 不能直接赋值
                (context.getImageData(e.offsetX, e.offsetY, 1, 1).data || []).forEach(item => {
                    params.push(item);
                });

                this.imgStyleColor = `rgb(${params[0]}, ${params[1]}, ${params[2]})`;
            });
        }

        // 键盘事件
        rgb (): string{//rgb颜色随机
			var r = Math.floor(Math.random()*256);
			var g = Math.floor(Math.random()*256);
			var b = Math.floor(Math.random()*256);
			var rgb = 'rgb('+r+','+g+','+b+')';
            return rgb;
        }
        radomPosition (): Object {
            let x = Math.random() * 900 as number;
            let y = Math.random() * 300 as number;
            if (x > 850) {
                x = 850;
            } else if (x < 50) {
                x = 50;
            }

            if (y > 250) {
                y = 250;
            } else if (y < 50) {
                y= 50;
            }

            return {
                x,
                y
            }
        }
        // val: Object  { console.error((val as any).key) } 或者 val: { [key: string] : '' }
        doKeyDown (val: KeyboardEvent): void {
            const canvas = document.getElementById('documentOrWindow') as HTMLCanvasElement;
            const context = canvas.getContext('2d') as CanvasRenderingContext2D;
            context.save();
            context.beginPath();
            
            let color = this.rgb() as string;
            context.fillStyle = color;
            context.textAlign = 'center';
            context.font = `${(Math.random() * 30) + 20}px sans-serif`;

            // 这里 any 可能不太好
            let positon = this.radomPosition() as Position;
            console.error(context.font, positon);
            context.fillText(val.key, positon.x, positon.y);
            context.restore();

        }
        drawTabCanvas (): void {
            const canvas = document.getElementById('tabindex') as HTMLCanvasElement;
            canvas.addEventListener('click', () => {
                canvas.setAttribute('tabindex', '12');
                canvas.focus();
            });
            canvas.focus();
        }
        tabIndexTest (e: KeyboardEvent): void {
            const canvas = document.getElementById('tabindex') as HTMLCanvasElement;
            const context = canvas.getContext('2d') as CanvasRenderingContext2D;
            context.save();
            context.beginPath();
            
            let color = this.rgb() as string;
            context.fillStyle = color;
            context.textAlign = 'center';
            context.font = `${(Math.random() * 30) + 20}px sans-serif`;

            // 这里 any 可能不太好
            let positon = this.radomPosition() as Position;
            context.fillText(e.key, positon.x, positon.y);
            context.restore();
        }
        mounted() {
           this.drawContain();
           this.drawCircle();
           this.drawImage();
           window.addEventListener('keydown', this.doKeyDown, true);
           this.drawTabCanvas();
        }
    }
</script>
<style lang="scss">
.canvas-test-event{
    margin: 0 auto;
    width: 100%;
    padding: 0 0;
    box-sizing: border-box;
    .contain-canvas{
        canvas{
            background: lightgrey;
            display: inline-block;
        }
        .info{
            display: inline-block;
            min-width: 300px;
            height: 30vw;
            min-height: 300px;
            max-height: 300px;
            width: calc(100% - 40vw);
            box-sizing: border-box;
            font-size: 2rem;
            font-weight: 700;
            vertical-align: top;
            padding-left: 50px;
        }
    }
    .contain-canvas-image{
        .info{
            display: inline-block;
            width: calc(100% - 40vw);
            min-width: 300px;
            box-sizing: border-box;
            font-size: 2rem;
            font-weight: 700;
            vertical-align: top;
            padding-left: 50px;
            .image-color{
                span{
                    display: inline-block;
                    height: 40px;
                    width: 40px;
                    margin-right: 10px;
                    vertical-align: middle;
                }
            }
        }
    }
    .documentOrWindow{
        height: 300px;
        width: 100%;
        background: #f4f4f4;
    }
    #imageContain{
        width: 300px;
    }
}
</style>